#include <iostream>
#include <sys/select.h>
#include <string>
#include "Sock.hpp"

#define NUM (sizeof(fd_set) * 8)

int fd_array[NUM]; //内容>=0，合法的fd，如果是-1，该位置就没有fd

static void Usage(std::string proc)
{
    std::cout << "Usage:" << proc << "port" << std::endl;
}

//./select_server 8080
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(1);
    }
    u_int16_t port = atoi(argv[1]);
    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, port);
    Sock::Listen(listen_sock);
    for (int i = 0; i < NUM; i++)
    {
        fd_array[i] = -1;
    }

    fd_set rfds;
    fd_array[0] = listen_sock;
    for (;;)
    {
        FD_ZERO(&rfds); //清空读事件位图
        int max_fd = fd_array[0];

        for (int i = 0; i < NUM; i++)
        {
            if (fd_array[i] == -1)
                continue;
            //下面都是合法的fd
            FD_SET(fd_array[i], &rfds); //将fd设置进rfds
            // max_fd记录最大文件描述符
            if (max_fd < fd_array[i])
            {
                max_fd = fd_array[i];
            }
        }

        struct timeval timeout = {5, 0}; // 5s

        //第一个参数是最大文件描述符加1，是因为文件描述符要放到位图中，+1方便计算
        int n = select(max_fd + 1, &rfds, nullptr, nullptr, nullptr);
        switch (n)
        {
        case -1:
            std::cerr << "select error" << std::endl;
            break;
        case 0:
            std::cerr << "select timeout" << std::endl;
            break;
        default:
            std::cout << "有事件就绪了！" << std::endl;
            for (int i = 0; i < NUM; i++)
            {
                if (fd_array[i] == -1)
                    continue;
                //下面的fd都是合法的fd，但是不一定就绪
                //利用FD_ISSET检测是否就绪
                if (FD_ISSET(fd_array[i], &rfds))
                {
                    std::cout << "sock:" << fd_array[i] << "上面有读时间可以读取了" << std::endl;
                    //读事件就绪，就绪的fd就是fd_array[i]
                    if (fd_array[i] == listen_sock)
                    {
                        std::cout << "listen_sock:" << listen_sock << "有了新链接到来" << std::endl;
                        //表示有链接到来,accept
                        int sock = Sock::Accept(listen_sock);
                        if (sock >= 0)
                        {
                            std::cout << "listen_sock:" << listen_sock << "获取新链接成功" << std::endl;
                            //获取链接成功,并不意味着有数据到来
                            int pos = 1;
                            for (; pos < NUM; pos++)
                            {
                                //等于-1表示该位置没有被使用
                                if (fd_array[pos] == -1)
                                    break;
                            }
                            // 1、找到了位置
                            // 2、找完了也没有找到位置
                            if (pos < NUM)
                            {
                                //找到了
                                std::cout << "新链接：" << sock << "已经被添加到数组[" << pos << "]的位置" << std::endl;
                                fd_array[pos] = sock;
                            }
                            else
                            {
                                //没找到,说明服务器已经满载，没法处理新请求
                                std::cout << "服务器满载，稍后再试" << std::endl;
                                close(sock);
                            }
                        }
                    }
                    else
                    {
                        std::cout << "sock:" << fd_array[i] << "上面有普通读取" << std::endl;
                        // 普通的sock读时间就绪,可以进行读取
                        char recv_buffer[1024] = {0};
                        ssize_t s = recv(fd_array[i], recv_buffer, sizeof(recv_buffer) - 1, 0);
                        if (s > 0)
                        {
                            recv_buffer[s] = '\0';
                            std::cout << "client[ " << fd_array[i] << "]# " << recv_buffer << std::endl;
                        }
                        else if (s == 0)
                        {
                            std::cout << "sock:" << fd_array[i] << "关闭了！" << std::endl;
                            //对端关闭链接
                            close(fd_array[i]); //直接关闭链接
                            std::cout << "已经在数组下标fd_array[" << i << "]"
                                      << "中去掉了sock:" << fd_array[i] << std::endl;
                            fd_array[i] = -1;   //位置值改为-1，表示该位置可以被使用了
                        }
                        else
                        {
                            //读取失败
                        }
                    }
                }
            }
            break;
        }
    }

    return 0;
}